package com.xuan.common.file.service;


import cn.hutool.core.lang.ObjectId;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.xuan.common.core.constants.CacheConstants;
import com.xuan.common.core.constants.Constants;
import com.xuan.common.core.exception.BusinessException;
import com.xuan.common.core.utils.ThreadLocalUtils;
import com.xuan.common.redis.service.RedisService;
import com.xuan.common.file.config.OSSProperties;
import com.xuan.common.file.domain.OSSResult;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

@Slf4j
@Service
@RefreshScope
public class OSSService {


    @Resource
    private OSSProperties properties;

    @Resource
    private RedisService redisService;

    @Resource
    private OSS ossClient;

    @Value("${file.test}")
    private boolean test;       // 本地开发阶段,不进行上传次数限制

    @Value("${file.upload-limit}")
    private Long uploadLimit;


    public OSSResult uploadFile(MultipartFile file) throws IOException {
        // 本地开发阶段,不进行上传次数限制
        if (!test) {
            checkUploadCount();
        }
        String fileName = file.getOriginalFilename();
        if (StringUtils.isBlank(fileName)) {
            fileName = "a.png";
        }
        // 获取文件后缀
        String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            return upload(extName, inputStream);
        } catch (IOException e) {
            log.error("OSS upload file error", e);
            throw new BusinessException("文件上传失败");
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }


    private void checkUploadCount() {
        Long userId = ThreadLocalUtils.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new BusinessException("当前登录状态已过期");
        }
        String uploadKey = CacheConstants.USER_UPLOAD_TIMES_KEY + userId;
        Long uploadTimes = redisService.getCacheObject(uploadKey, Long.class);
        if (uploadTimes != null && uploadTimes > uploadLimit) {
            throw new BusinessException("已达到当日最大上传次数,文件上传失败");
        }
    }


    private OSSResult upload(String extName, InputStream inputStream) {
        // 填写Object完整路径(包括目录 + 文件名)，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
        String uniKey = ObjectId.next();        // 为当前文件生成唯一标识,防止用户上传时文件名重复
        String objectName = properties.getPathPrefix() + uniKey  + "." + extName;
        // 创建PutObjectRequest对象。
        PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), objectName, inputStream);
        // 上传时设置存储类型和访问权限:可读权限
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setObjectAcl(CannedAccessControlList.PublicRead);
        putObjectRequest.setMetadata(metadata);

        PutObjectResult result;
        try {
            // 上传图片/视频等。
            result = ossClient.putObject(putObjectRequest);
        } catch (Exception oe) {
            log.error("OSS put object error", oe);
            throw new BusinessException("文件上传失败");
        }
        return assembleOSSResult(uniKey + "." + extName, result);
    }


    private OSSResult assembleOSSResult(String uniKey, PutObjectResult result) {
        OSSResult ossResult = new OSSResult();
        if (result != null && StringUtils.isNotBlank(result.getRequestId())) {
            ossResult.setName(uniKey);
            ossResult.setSuccess(true);
        } else {
            ossResult.setSuccess(false);
        }
        return ossResult;
    }

}
